#include <stdlib.h>
#include <iostream>
#include "system/gamemain.h"
#include "system/window.h"
#include "graphics/directx9/renderer_directx9.h"
#include "graphics/effect_basic.h"
#include "util/frametimer.h"
#include "entity/entity.h"
#include "framework/framework.h"
#include "Box2D/Box2D.h"
#include "graphics/debugdraw.h"
#include "physics/physicsrenderer.h"
#include "physics/physicsworld.h"
#include "gameentity.h"
#include "ship.h"
#include "planet.h"
#include "input/iinputmanager.h"
#include "jobs/jobmanager.h"
#include "moveeffect.h"

class HeightmapTest
{
public:
	static const int SIZE = 129;

	void Midpoint(int low, int high, float offset = -1.0f)
	{
		int mid = ((high - low) / 2) + low;

		// end condition
		if (mid == low || mid == high || mid == 0)
		{
			return;
		}

		/*if (offset == -1.0f)
		{
			float offsetMin = m_heightmap[low];
			float offsetMax = m_heightmap[high];
			float halfOffset = (offsetMax - offsetMin) * 0.5f;
			float randOffset = (gecko::Rand2<float>() * halfOffset) * 0.6f;
			offset = offsetMin + halfOffset + randOffset;
		}*/

		float midHeight = m_heightmap[low] + ((m_heightmap[high] - m_heightmap[low]) * 0.5f);
		
		float randOffset = gecko::Rand<float>() * 1.5f;
		m_heightmap[mid] = randOffset * (midHeight + offset);


		// recurse into left-hand half
		Midpoint(low, mid, offset * 0.25f);

		// recurse into left-hand half
		Midpoint(mid, high, offset * 0.25f);
	}

	HeightmapTest()
	{
		// todo, make sure size is power of 2 plus 1

		m_heightmap.reserve(SIZE);
		
		// init
		for (unsigned int i = 0; i < SIZE; i++)
		{
			m_heightmap.push_back(0.0f);
		}

		for (unsigned int i = 1; i < SIZE; i++)
		{
			m_heightmap[i] = m_heightmap[i - 1] + (gecko::Rand2<float>() * 0.1f);
			m_heightmap[i] = gecko::Max<float>(m_heightmap[i], 0.0f);
		}

		//int low = 0;
		//int high = SIZE - 1;
		//Midpoint(low, high, 1.0f);
	}

	void Render()
	{
		float renderWidth = 0.1f;

		for (unsigned int i = 0; i < SIZE - 1; i++)
		{
			gecko::Vector3f start(i * renderWidth, m_heightmap[i], 0.0f);
			gecko::Vector3f end((i + 1) * renderWidth, m_heightmap[i + 1], 0.0f);
			gecko::DebugDrawManager::Get().Line3D(start, end, gecko::Colour::White(), gecko::Colour::White());
		}
	}

	std::vector<float> m_heightmap;
};

// the entry point for the game, houses systems and calls out to user specified stuff
class GameFramework : public gecko::Framework, public gecko::IInputListener
{
protected:
	virtual void OnAction(gecko::InputEvent & action)
	{
		static const float PAD_ZOOM = 0.1f;

		switch (action.inputID)
		{
		case MyGameAction_Thrust:
			m_ship.DoThrust(action.value1);
			break;

		case MyGameAction_TurnShipLeft:
			m_ship.Turn(-action.value1);
			break;

		case MyGameAction_TurnShipRight:
			m_ship.Turn(action.value1);
			break;

		case MyGameAction_ZoomIn:
			// update zoom
			m_zoomDistance -= action.value1 * PAD_ZOOM;
			m_zoomDistance = m_zoomDistance < 5.0f ? 5.0f : m_zoomDistance;
			break;

		case MyGameAction_ZoomOut:
			// update zoom
			m_zoomDistance += action.value1 * PAD_ZOOM;
			m_zoomDistance = m_zoomDistance < 5.0f ? 5.0f : m_zoomDistance;
			break;
		}
	}

	// override by game-code
	virtual void OnInitialise()
	{
		TheInputManager->AddListener(this);

		//TheRenderer->SetRenderMode(gecko::IRenderer::RM_WIREFRAME);
		m_simpleShader = new gecko::Effect_Basic(TheRenderer);
		m_simpleShader->EnableAlpha(true);
		m_simpleShader->SetSrcAlpha(gecko::IRenderer::BM_SRCALPHA);
		m_simpleShader->SetDestAlpha(gecko::IRenderer::BM_INVSRCALPHA);
		
		m_physicsWorld = new gecko::PhysicsWorld(gecko::Vector3f(0.0f, 0.0f, 0.0f));

		// initialise ship
		m_ship.Initialise(m_simpleShader);
		m_ship.InitialisePhysics(*m_physicsWorld);

		// initialise planets
		m_planets = new Planet[NUM_PLANETS];
		for ( int i = 0; i < NUM_PLANETS; i++ )
		{
			m_planets[i].Initialise(m_simpleShader);
			m_planets[i].InitialisePhysics(*m_physicsWorld);
		}

		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadA, MyGameAction_Thrust);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadY, MyGameAction_EnterExit);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadLThumbLeft, MyGameAction_TurnShipLeft);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadLThumbRight, MyGameAction_TurnShipRight);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadDLeft, MyGameAction_TurnShipLeft);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadDRight, MyGameAction_TurnShipRight);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadRThumbUp, MyGameAction_ZoomIn);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadRThumbDown, MyGameAction_ZoomOut);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_PadRightTrigger, MyGameAction_Thrust);

		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_LeftArrow, MyGameAction_TurnShipLeft);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_RightArrow, MyGameAction_TurnShipRight);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_UpArrow, MyGameAction_Thrust);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_Q, MyGameAction_ZoomIn);
		TheInputManager->SetActionMapping(MyGameActionContext_Ship, gecko::IInputManager::Evt_A, MyGameAction_ZoomOut);

		gecko::DebugDrawManager::Get().Register(TheRenderer);

		m_cameraTarget = &m_ship;
		m_zoomDistance = 10.0f;

		m_moveEffect.Initialise(20.0f, m_simpleShader);
		m_moveEffect.Attach(&m_ship);

		//m_jobManager = new gecko::JobManager(4);
	}

	// override by game-code
	virtual void OnUpdate(float dt)
	{
		for ( int i = 0; i < NUM_PLANETS; i++ )
		{
			m_planets[i].Update(dt);
			m_ship.AccumulateGravityForce(m_planets[i]);
		}

		m_physicsWorld->Update(dt);

		m_ship.Update(dt);
	}

	// override by game-code
	virtual void OnRender(gecko::IRenderer * renderer)
	{
		gecko::Vector3f myPosition;
		m_cameraTarget->GetPosition(myPosition);

		// rotate camera
		gecko::Matrix44 rotation;
		rotation.SetupRotationZ(0.0f);
		gecko::Vector3f cameraUp = gecko::Vector3f(0.0f, 1.0f, 0.0f) * rotation;		

		gecko::Vector3f zoomFactor(0.0f, 0.0f, m_zoomDistance);
		renderer->SetCamera(myPosition + zoomFactor, myPosition, cameraUp, 45.0f);

		m_moveEffect.Render(renderer);

		// draw planets
		for ( int i = 0; i < NUM_PLANETS; i++ )
		{
			m_planets[i].Render(renderer);
		}

		//m_heightmap.Render();

		// draw ship
		m_ship.Render(renderer);

		//m_physicsWorld->RenderDebug();
		gecko::DebugDrawManager::Get().SwapBuffers();
	}

	static const int NUM_PLANETS = 2;
	Planet* m_planets;
	Ship m_ship;
	gecko::Effect_Basic * m_simpleShader;
	gecko::PhysicsRenderer m_physicsRender;
	gecko::PhysicsWorld * m_physicsWorld;
	GameEntity * m_cameraTarget;
	float m_zoomDistance;
	//gecko::JobManager * m_jobManager;
	HeightmapTest m_heightmap;
	MoveEffect m_moveEffect;
};

int main(int /*argc*/, char** /*argv*/)
{	
	GameFramework gameFramework;
	gameFramework.Run(1280, 720, 32, false);
	//gameFramework.Run(1920, 1080, 32, true);
	return 0;
}



